﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.ServiceModel;
using System.Text.RegularExpressions;

namespace RentIt06
{
    public class PublisherSession : IPublisherSession
    {
        Account account;

        public Account Login(string email, string password) {
            var dbResult = from u in new db.DataMapDataContext().Users
                           where u.email == email
                              && u.password == Account.hash(password, email)
                              && (u.role == PermissionGroup.Publisher.ToString() || u.role == PermissionGroup.Admin.ToString())
                           select u;
            if (dbResult.Any()) account = new Account(dbResult.First());
            else throw new FaultException(new FaultReason("Wrong password, email or user has insufficent access"), new FaultCode("401"));
            return account;
        }

        private void checkIfLoggedIn()
        {
            if (account.Equals(default(Account))) throw new FaultException("You are not logged in");
        }

        public Media ReadMedia(int id)
        {
            checkIfLoggedIn();
            var dbResult = from m in new db.DataMapDataContext().Medias
                           where m.id == id
                              && (m.published || m.owner == account.Id || account.Permission == PermissionGroup.Admin) //you need to be either owner or admin, or the media needs to be public.
                           select m;
            if (dbResult.Any()) return new Media(dbResult.First());
            throw new FaultException(new FaultReason("Media with id " + id + " not found."), new FaultCode("404"));
        }

        public int CreateMedia() {
            checkIfLoggedIn();
            var map = new db.DataMapDataContext();
            var media = new db.Media {
                    owner = account.Id,
                    title = "",
                    author = "",
                    category = "",
                    releaseDate = new DateTime(),
                    canBeBought = false,
                    canBeRented = false,
                    fileName = "Default.file",
                    purchasePrice = 0,
                    rentingPrice = 0,
                    published = false,
                    mediaType = MediaType.Unspecified.ToString()
                };
            map.Medias.InsertOnSubmit(media);
            map.SubmitChanges();
            return media.id;
        }

        public int GetUploadToken(int id, byte[] hash) {
            checkIfLoggedIn();
            var map = new db.DataMapDataContext();
            var result = (from m in map.Medias
                          where m.id == id &&
                          (m.owner == account.Id || account.Permission == PermissionGroup.Admin)
                          select m);
            if (!result.Any())
                throw new FaultException(new FaultReason("Media with id " + id + " not found."), new FaultCode("404"));
            var media = result.First();
            if (media.published)
                throw new FaultException(new FaultReason("Media with id " + id + " has already been published."));
            var upload = new db.Upload {
                media = id,
                hash = hash,
                expires = DateTime.Now.AddMinutes(30)
            };
            map.Uploads.InsertOnSubmit(upload);
            map.SubmitChanges();
            return upload.id;
        }

        public void UpdateMedia(Media mediaIn) {
            checkIfLoggedIn();
            //Admin can update any media?
            if (!LegalFileName(mediaIn.Filename)) throw new FaultException(new FaultReason("The filename: \"" + mediaIn.Filename + "\" is not a legal filename"));
            var map = new db.DataMapDataContext();
            var result = (from m in map.Medias
                          where m.id == mediaIn.Id
                          && (account.Permission == PermissionGroup.Admin || m.owner == account.Id) //you need to be admin or owner
                          select m);
            if (!result.Any())
                throw new FaultException(new FaultReason("Media with id " + mediaIn.Id + " not found."), new FaultCode("404"));
            var media = result.First();
            media.title = mediaIn.Title ?? "";
            media.author = mediaIn.Author ?? "";
            media.category = mediaIn.Category ?? "";
            media.releaseDate = mediaIn.ReleaseDate;
            media.canBeRented = mediaIn.CanBeRented;
            media.canBeBought = mediaIn.CanBeBought;
            if (mediaIn.RentingPrice >= 0) media.rentingPrice = mediaIn.RentingPrice;
            if (mediaIn.PurchasePrice >= 0) media.purchasePrice = mediaIn.PurchasePrice;
            media.fileName = mediaIn.Filename ?? "";
            media.mediaType = mediaIn.MediaType.ToString();
            map.SubmitChanges();
        }

        public void PublishMedia(int id) {
            checkIfLoggedIn();
            SetPublished(id,true);
        }

        public void UnPublishMedia(int id) {
            checkIfLoggedIn();
            SetPublished(id,false);
        }

        private void SetPublished(int id, bool published) {
            //Admin can publish any media?
            var map = new db.DataMapDataContext();
            var result = from media in map.Medias
                            where media.id == id
                            && (account.Permission == PermissionGroup.Admin || media.owner == account.Id) //you need to be admin or owner
                            select media;
            if (!result.Any())
            {
                throw new FaultException(new FaultReason("Media with id " + id + " not found."), new FaultCode("404"));
            }
            result.First().published = published;
            map.SubmitChanges();
        }


        public List<Media> GetMyPublishedMedia() {
            checkIfLoggedIn();
            return (from m in new db.DataMapDataContext().Medias
                    where m.owner == account.Id
                    select m).Select(m => new Media(m)).ToList();
        }

        private static bool LegalFileName(string str) {
            return Regex.IsMatch(str, @"^[a-zA-Z0-9\-_]+\.[a-zA-Z0-9]+$");
        }

        public void Logout() {
            // DONE!
        }
    }
}
